package com.hcg.multidatasourceofanno2.config.datasource;


import com.hcg.multidatasourceofanno2.common.function.CommonException;
import com.hcg.multidatasourceofanno2.common.utils.JudgeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 态数据源注册
 */
@Slf4j
public class DynamicDataSourceRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private Environment environment;
    private String defaultDataSourceName = "main";
    // 数据源
    private DataSource defaultDataSource;
    private Map<String, DataSource> dynamicDataSources = new HashMap();
    // 如配置文件中未指定数据源类型，使用该默认值
    private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";

    public DynamicDataSourceRegistrar() {
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(this.environment, "spring.datasource.");
        String enable = propertyResolver.getProperty("enabled");
        if (JudgeUtils.isBlank(enable)) {
            enable = "true";
        }
        if (Boolean.valueOf(enable)) {
            this.findDefaultDataSource(importingClassMetadata);
            this.registerDataSources();
            this.registerDynamicDataSource(importingClassMetadata, registry);
        }
    }

    public void findDefaultDataSource(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> defaultAttrs = importingClassMetadata.getAnnotationAttributes(EnableDynamicDataSource.class.getName(), true);
        if (defaultAttrs.containsKey("defaultDataSource")) {
            String defaultDataSource = (String) defaultAttrs.get("defaultDataSource");
            if (JudgeUtils.isNotBlank(defaultDataSource)) {
                this.defaultDataSourceName = defaultDataSource;
            }
        }

        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(this.environment, "spring.datasource.");
        if (JudgeUtils.isNotBlank(propertyResolver.getProperty("defaultDataSource"))) {
            this.defaultDataSourceName = propertyResolver.getProperty("defaultDataSource");
        }
        if (log.isDebugEnabled()) {
            log.debug("default data source is {}", this.defaultDataSourceName);
        }
    }

    private void registerDataSources() {
        RelaxedPropertyResolver datasourcesPropertyResolver = new RelaxedPropertyResolver(this.environment/*, "dataSource."*/);
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(this.environment, "cn.hcg.");
        String dsPrefixs = propertyResolver.getProperty("names");
        if (JudgeUtils.isBlank(dsPrefixs)) {
            throw new CommonException("dynamic data source is blank, please config the property \"cn.hcg.datasource.names\" in configuration file.");
        } /*else {
            String[] var4 = dsPrefixs.split(",");
            int var5 = var4.length;

            for (int var6 = 0; var6 < var5; ++var6) {
                String dsPrefix = var4[var6];
                Map<String, Object> map = datasourcesPropertyResolver.getSubProperties(dsPrefix + ".");
                DataSource ds = this.initDataSource(map);
                if (JudgeUtils.equals(this.defaultDataSourceName, dsPrefix)) {
                    this.defaultDataSource = ds;
                } else {
                    this.dynamicDataSources.put(dsPrefix, ds);
                }

                this.dataBinder(ds, dsPrefix);
                if (logger.isInfoEnabled()) {
                    logger.info("create data source {}", ds);
                }
            }
        }*/
        for (String dsPrefix : dsPrefixs.split(",")) {
            Map<String, Object> map = datasourcesPropertyResolver.getSubProperties(dsPrefix + ".");
            DataSource ds = initDataSource(map);

            if (JudgeUtils.equals(this.defaultDataSourceName, dsPrefix)) {
                this.defaultDataSource = ds;
            } else {
                this.dynamicDataSources.put(dsPrefix, ds);
            }
            dataBinder(ds, dsPrefix);
            if (log.isInfoEnabled()) {
                log.info("create data source {}", ds);
            }
        }
    }

    private void registerDynamicDataSource(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap();
        targetDataSources.put(this.defaultDataSourceName, this.defaultDataSource);
        targetDataSources.putAll(this.dynamicDataSources);
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        beanDefinition.setPrimary(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", this.defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition);
    }

    /**
     * 创建DataSource
     * @param map
     * @return
     */
    public DataSource initDataSource(Map<String, Object> map) {
        String driverClassName = map.get("driverClassName").toString();
        String url = map.get("url").toString();
        String username = map.get("username").toString();
        String password = map.get("password").toString();
        String dsType = map.get("type").toString();
        if (dsType == null){
            dsType = DATASOURCE_TYPE_DEFAULT.toString();// 默认DataSource
        }
        DataSource dataSource = null;
        try {
            Class<DataSource> dataSourceType = (Class<DataSource>) Class.forName(dsType);
            dataSource = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType).build();
            return dataSource;
        } catch (ClassNotFoundException var10) {
            if (log.isErrorEnabled()) {
                log.error("init data source error.", var10);
            }
            throw new CommonException(var10);
        }
    }

    private void dataBinder(DataSource dataSource, String dsName) {
        String[] disallowedFields = new String[]{"driverClassName", "url", "username", "password", "type"};
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        dataBinder.setIgnoreNestedProperties(false);
        dataBinder.setIgnoreInvalidFields(false);
        dataBinder.setIgnoreUnknownFields(true);
        dataBinder.setDisallowedFields(disallowedFields);
        Map<String, Object> values = (new RelaxedPropertyResolver(this.environment, "dataSource." + dsName)).getSubProperties(".");
        dataBinder.bind(new MutablePropertyValues(values));

    }
}